<?php
class XA_Db_Table_Sybase extends XA_Db_Table {



   public function __construct($table_name) {
      parent::__construct($table_name);

      $pk_index = 1;

      while ($pk_name = xa::query("SELECT INDEX_COL('$table_name', 2, $pk_index)")->val()) {
         $this->primary_key_list[] = $pk_name;
         $pk_index++;
      }

      $sql = "
         SELECT
              c.name
            , c.length AS size
            , (c.status & 8) AS nullable
            , (c.status & 128) AS serial
            , t.name AS Type
         FROM
            syscolumns c
            JOIN sysobjects o ON c.id = o.id
            LEFT JOIN systypes t ON t.usertype = c.usertype
         WHERE
            o.name = '$table_name'
         ORDER BY
            c.colid ASC
      ";

      foreach (xa::query($sql)->all('name') as $field_name => $field_desc) {

         if ($field_desc['serial']) {
            $this->auto_increment_field = $field_name;
         }

         $field = array();

         $field['null'] = (bool) $field_desc['nullable'];

         $field['size'] = (int) $field_desc['size'];

         /*
         if (!empty($field_desc['Default']) || ($field_desc['Default'] === '0')) {
            $field['default'] = $field_desc['Default'];
         }
         */

         $field['unsigned'] = false;

         switch (true) {
            case (substr_count($field_desc['Type'], 'char')): {
               $field['type'] = 'char';
               break;
            }
            case (substr_count($field_desc['Type'], 'int')): {
               $field['type'] = 'int';
               break;
            }
            case ($field_desc['Type'] === 'float'): {
               $field['size'] = 20;
               $field['type'] = 'float';
               break;
            }
            case ($field_desc['Type'] === 'datetime'): {
               $field['type'] = 'datetime';
               break;
            }
            case ($field_desc['Type'] === 'date'): {
               $field['type'] = 'date';
               break;
            }
            case ($field_desc['Type'] === 'time'): {
               $field['type'] = 'time';
               break;
            }
            case ($field_desc['Type'] === 'tinytext'): {
               $field['type'] = 'tinytext';
               break;
            }
            case (substr_count($field_desc['Type'], 'text')): {
               $field['type'] = 'text';
               break;
            }
            case (stripos($field_desc['Type'], 'decimal') === 0): {
               $field['type'] = 'float';
               break;
            }
            default: {
               e("\nUNSUPPORTED SYBASE COLUMN TYPE: ".$field_desc['Type']."\n");
            }
         }

         $this->field_list[$field_name] = $field;
      }
   }



   public function search($row) {
      $query = new XA_Query($this->table_name);
      $row = $this->filter($row);
      foreach ($row as $field_name => $value) {
         $query->conditions[$field_name] = "$field_name = '".xa_db::esc($value)."'";
      }
      return xa::query($query)->all();
   }



   public function find($row) {
      if (!is_array($row) && count($this->primary_key_list) === 1) {
         $row = array($this->primary_key_list[0] => $row);
      }
      $query = new XA_Query($this->table_name);
      $query->limit = 1;
      $row = $this->filter($row);
      foreach ($row as $field_name => $value) {
         if ($value === null) {
            $query->conditions[$field_name] = "$field_name IS NULL";
         } else {
            $query->conditions[$field_name] = "$field_name = '".xa_db::esc($value)."'";
         }
      }
      return xa::query($query)->row();
   }



   public function exists($row) {
      if (!is_array($row) && count($this->primary_key_list) === 1) {
         $row = array($this->primary_key_list[0] => $row);
      }

      $query = new XA_Query($this->table_name);
      $query->fields = array('exists' => '1');
      $query->limit = 1;
      $row = $this->filter($row);

      foreach ($row as $field_name => $value) {
         $query->conditions[$field_name] = "$field_name = '".xa_db::esc($value)."'";
      }

      return (bool)xa::query($query)->row();
   }



   public function count($row) {
      $query = new XA_Query($this->table_name);
      $query->fields = array('count' => "COUNT(1)");
      $row = $this->filter($row);

      foreach ($row as $field_name => $value) {
         $query->conditions[$field_name] = "$field_name = '".xa_db::esc($value)."'";
      }

      return intval(xa::query($query)->val());
   }



   public function prepare($value, $field_name) {
      $field = $this->field_list[$field_name];

      if ($field['type'] === 'int') {
         if ($field['null'] && $field['unsigned'] && empty($value)) {
            return 'NULL';
         }
         return intval($value);
      }

      if ($field['type'] === 'float') {
         if ($field['null'] && !xa_in::float($value)) {
            return 'NULL';
         }
         return $value;
      }

      if ($field['null'] && (($value === '') || is_null($value))) {
            return 'NULL';
      }

      return "'".xa_db::esc($value)."'";
   }



   public function insert($row) {
      $row = $this->filter($row);

      if (!empty($this->auto_increment_field)) {
         unset($row[$this->auto_increment_field]);
      }

      $this->error_list = $this->error_list + $this->validate($row);

      if ($this->error_list) {
         return false;
      } else {
         if (empty($this->auto_increment_field)) {
            foreach ($this->primary_key_list as $pk_name) {
               if (empty($row[$pk_name])) {
                  $this->error_list[$pk_name]['pk_not_set'] = true;
               }
            }
         }

         $value_list = array();
         if (!$this->error_list) {
            foreach ($row as $field_name => $value) {
               $value_list[] = $this->prepare($value, $field_name);
            }
            return xa::query("INSERT INTO ".$this->table_name." (".implode(', ', array_keys($row)).") VALUES (".implode(', ', $value_list).")");
         }

         return false;
      }
   }




   public function update($row) {
      $row = $this->filter($row);
      $this->error_list = $this->error_list + $this->validate($row);

      if ($this->error_list) {
         e($this->error_list);
         return false;
      } else {
         $condition_list = array();
         $value_list = array();

         foreach ($this->primary_key_list as $pk_name) {
            if (empty($row[$pk_name])) {
               $this->error_list[$pk_name]['pk_not_set'] = true;
            }
            $condition_list[] = $pk_name." = ".$this->prepare($row[$pk_name], $pk_name);
            unset($row[$pk_name]);
         }

         if (!$this->error_list) {
            foreach ($row as $field_name => $value) {
               $value_list[] = $field_name." = ".$this->prepare($value, $field_name);
            }

            if ($value_list) {
               return xa::query("UPDATE ".$this->table_name." SET ".implode(', ', $value_list)." WHERE ".implode(' AND ', $condition_list));
            } else {
               return true;
            }
         }

         return false;
      }
   }



   public function validate($row, $skip_pk = false) {
      if ($skip_pk) {
         foreach ($this->primary_key_list as $pk) {
            unset($row[$pk]);
         }
      }
      $row = $this->filter($row);
      $error_list = array();

      foreach ($row as $field_name => $value) {
         $field = $this->field_list[$field_name];

         switch (true) {

            case ($field['type'] === 'int'): {
               if ($value === '') {
                  if ($field['null']) {
                     break;
                  }
                  $error_list[$field_name]['empty'] = true;
                  break;
               }
               if (!empty($field['unsigned']) && !xa_in::nat($value) && ($value !== '0') && ($value !== 0)) {
                  if ($field['null'] && ($value === '' || $value === null)) {
                     break;
                  }
                  $error_list[$field_name]['not_natural'] = true;
               } elseif ((xa_in::int($value) === false) && !$field['null']) {
                  if ($field['null'] && ($value === '' || $value === null)) {
                     break;
                  }
                  $error_list[$field_name]['not_integer'] = true;
               }
               break;
            }

            case ($field['type'] === 'float'): {
               if (xa_in::float($value) === false && !$field['null']) {
                  $error_list[$field_name]['not_float'] = true;
               }
               break;
            }

            case ($field['type'] === 'datetime'): {
               if (!xa_in::datetime($value)) {
                  $error_list[$field_name]['not_datetime'] = true;
               }
               break;
            }

            case ($field['type'] === 'date'): {
               if (!xa_in::date($value)) {
                  $error_list[$field_name]['not_date'] = true;
               }
               break;
            }
         }
      }

      return $error_list;
   }



   public function getPrimaryKeyList() {
      return $this->primary_key_list;
   }



   public function getFieldList() {
      return $this->field_list;
   }
}
